home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / cyber5.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  68KB  |  1,973 lines

  1. /*
  2. AI Code for Invasion Force - an Explore/Conquer Strategic Wargame
  3. Copyright (C) 1996  Brannen Hough
  4.  
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19. /*
  20.    cyber5.c -- artificial intelligence module for Empire II
  21.  
  22. */
  23.  
  24. /* This file contains all the routines associated with AI Type #5.
  25. */
  26.  
  27.  
  28. #include <math.h>
  29. #include "global.h"
  30.  
  31. // Local global variables.  not in a .h file for others to use.
  32. extern short  Type;
  33. extern short  Destx;
  34. extern short  Desty;
  35.  
  36. #define SHOW_AI5_MSG
  37. #ifdef SHOW_AI5_MSG
  38. #define  DEBUG_AI5(string)  if (!rtEZRequestTags(string,"Continue|Abort", \
  39.      NULL,NULL, RTEZ_Flags,EZREQF_CENTERTEXT,RT_Window,map_window, \
  40.      RT_ReqPos,REQPOS_CENTERWIN,RT_LockWindow,TRUE,TAG_END )) \
  41.      { if (AIhandle != NULL)  unpost_it(AIhandle); AIhandle = NULL; \
  42.      clean_exit(0, NULL); }
  43. #endif
  44. #ifndef SHOW_AI5_MSG
  45. #define  DEBUG_AI5(string)
  46. #endif
  47.  
  48.  
  49.  
  50. // And a huge 'extern' section for all the unit type selection lists
  51. extern struct GovPrefs  AI5_CITY_DEF_AIR;
  52. extern struct GovPrefs  AI5_CITY_DEF_SEA;
  53. extern struct GovPrefs  AI5_CITY_SEARCH_FIRST;
  54. extern struct GovPrefs  AI5_CITY_SUPPORT_SEA;
  55. extern struct GovPrefs  AI5_CITY_SUPPORT_LAND;
  56. extern struct GovPrefs  AI5_ISLE_DEF_OWN;
  57. extern struct GovPrefs  AI5_ISLE_DEF_AIR;
  58. extern struct GovPrefs  AI5_ISLE_DEF_SEA;
  59. extern struct GovPrefs  AI5_ISLE_SEARCH_FIRST;
  60. extern struct GovPrefs  AI5_ISLE_SEARCH_OWN;
  61. extern struct GovPrefs  AI5_ISLE_SEARCH_AIR;
  62. extern struct GovPrefs  AI5_ISLE_SUPPORT;
  63. extern struct GovPrefs  AI5_TRANS_ATTACK;
  64. extern struct GovPrefs  AI5_TRANS_DEFEND;
  65. extern struct GovPrefs  AI5_CARR_ATT_SEA;
  66. extern struct GovPrefs  AI5_CARR_ATT_AIR;
  67. extern struct GovPrefs  AI5_BATT_ATTACK;
  68.  
  69. /***************************************************************
  70. *************** Production Routines  ***************************
  71. ***************************************************************/
  72. #define GET_REQ 10
  73. void  AI5_set_gov_prod (struct City *metro, struct GovNode *CityOwner)
  74. {
  75.   // change this to only ask for suggestions IF the city is in Support
  76.   //  mode.  All other times be selfish and produce our own units
  77.     struct GovNode *Gov = (struct GovNode *)GovList.mlh_Head;
  78.     struct GovReqs  allreqs[ GET_REQ ];
  79.     int i, lowest_pri, lowest_ind, total_pri = 0;
  80.     int rand_pri;
  81.  
  82.     /* Go through the list of Governors.  We find the 10 governors with
  83.        the highest priority requests, and pick one at random based on the
  84.        relative priorities (i.e. a request of priority 5 is 5 times
  85.        more likely to be honored than a request of priority 1) and make
  86.        that requested unit. */
  87.  
  88.     /* Lets do some error checking */
  89.     CityOwner->req.req_gov = -1;
  90.     CityOwner->req.priority = 0;
  91.     CityOwner->req.type = -1;
  92.  
  93.     /* Let's initialize the 10 requests */
  94.     for (i=0; i < GET_REQ; i++) {
  95.         allreqs[i].req_gov = -1;
  96.         allreqs[i].priority = 0;
  97.         allreqs[i].type = -1;
  98.     }
  99.  
  100.     for ( ; Gov->gnode.mln_Succ; Gov = (struct GovNode *)Gov->gnode.mln_Succ) {
  101.       if (Gov->owner == player) {
  102.        LastReq.req_gov = -1;
  103.        LastReq.priority = 0;
  104.        LastReq.type = -1;
  105.             AI5_get_gov_req (Gov, metro, CityOwner->type );
  106.        if( LastReq.priority > 0 ) {
  107.       /* Add it to the list if it is one of the top whatever */
  108.       lowest_pri = 1000;
  109.       lowest_ind = -1;
  110.       for( i=0; i < GET_REQ; i++ ) {
  111.           if( allreqs[i].priority < lowest_pri ) {
  112.          lowest_ind = i;
  113.          lowest_pri = allreqs[i].priority;
  114.           }
  115.       }
  116.       if( lowest_ind != -1 ) {
  117.           allreqs[lowest_ind].req_gov = LastReq.req_gov;
  118.           allreqs[lowest_ind].priority = LastReq.priority;
  119.           allreqs[lowest_ind].type = LastReq.type;
  120.       }
  121.        }
  122.    }
  123.     } /* End for */
  124.     /* Now go through the list and total up the priorities, then pick a
  125.        random number from 1 to the total */
  126.     for( i=0; i < GET_REQ; i++) {
  127.    total_pri += allreqs[i].priority;
  128.     }
  129.     rand_pri = RangeRand( (long) (total_pri -1) ) + 1;
  130.  
  131.     /* Now select the random one we picked */
  132.     lowest_ind = -1;
  133.     while( rand_pri > 0 ) {
  134.    rand_pri -= allreqs[++lowest_ind].priority;
  135.     }
  136.     if( (lowest_ind < 0) || (lowest_ind > (GET_REQ -1)) ) {
  137.    /* Error - out of bounds */
  138.    DEBUG_AI("Out of bounds for production selection randomizer")
  139.         /* Well, make another of whatever we were making. */
  140.    CityOwner->req.req_gov = CityOwner->ID;
  141.    if( ( metro->unit_type >=0 ) && (metro->unit_type <= CARRIER) ) {
  142.        CityOwner->req.type = metro->unit_type;
  143.    }
  144.    else {
  145.        /* Need to make sure we have a decent fall-back: make an
  146.           Infantry for the owning governor - we can always use
  147.           Infantry */
  148.        metro->unit_type = RIFLE;
  149.        CityOwner->req.type = RIFLE;
  150.    }
  151.         return;
  152.     }
  153.     else {
  154.    /* Make the selected type of unit */
  155.    CityOwner->req.priority = LastReq.priority;
  156.    CityOwner->req.type = LastReq.type;
  157.    CityOwner->req.req_gov = LastReq.req_gov;
  158.    sprintf(outbuf, "Producing a %s for Governor %ld",
  159.       UnitString[CityOwner->req.type], CityOwner->req.req_gov);
  160.    DEBUG_AI(outbuf)
  161.  
  162.    /* And we set the city itself to produce this */
  163.    if (metro->unit_type != CityOwner->req.type) {  /*making a new type*/
  164.        metro->unit_type = CityOwner->req.type;
  165.        /*Tooling up penalty */
  166.        metro->unit_wip = -1 * wishbook[metro->unit_type].build/5;
  167.        sprintf (outbuf, "Tooling Up the city to build a %s",
  168.            UnitString[metro->unit_type]);
  169.        DEBUG_AI(outbuf)
  170.    }
  171.    /* unit->wip has been set to zero elsewhere.  Don't zero out
  172.       production that has already been started when we haven't
  173.       changed the type. We have enough handicaps as it is.
  174.       */
  175.     }
  176.     return;
  177. }
  178.  
  179. void  AI5_get_gov_req( struct GovNode* Gov, struct City* metro,
  180.       enum GovType type)
  181. {
  182.   if(( Gov->type == GOV_CITY ) || ( Gov->type == GOV_PORT )) {
  183.       AI5_get_city_req( Gov, metro, type );
  184.   }
  185.   if( Gov->type == GOV_ISLAND ) {
  186.       AI5_get_island_req( Gov, metro, type );
  187.   }
  188.   if( Gov->type == GOV_TRANSPORT ) {
  189.         AI5_get_transport_req( Gov, metro, type );
  190.   }
  191.   if( Gov->type == GOV_CARRIER ) {
  192.         AI5_get_carrier_req( Gov, metro, type );
  193.   }
  194.   if( Gov->type == GOV_BATTLESHIP ) {
  195.         AI5_get_battleship_req( Gov, metro, type );
  196.   }
  197.   if( LastReq.type != -1 ) {
  198.         // We selected a type to build, set our ID as owner
  199.         // This way, one less thing to be set by all the other routines.
  200.         LastReq.req_gov = Gov->ID;
  201.   }
  202. }
  203.  
  204. void  AI5_get_city_req( struct GovNode* Gov, struct City* metro,
  205.       enum GovType type )
  206. {
  207.     // What mode are we in now?
  208.     switch (Gov->mode) {
  209.     case GOV_TAKEN: {
  210.       // We must have ground troops if they can reach us
  211.       //  Otherwise, AIRCAV
  212.       // if there is a path to this Gov from the city by Infantry,
  213.       //    make Infantry.
  214.       //    If there is a path from this Gov to the city by
  215.       //    Armor AND Armor can manuever,
  216.       //       make Armor instead.
  217.       // else (there is no path for Infantry)
  218.       //    if distance from Gov to city is less than 13
  219.       //       make Aircav instead.
  220.       // This is someone elses city, so let's see what we can get
  221.       int InfAccess;
  222.       int ArmorAccess;
  223.       long dist;
  224.       AI5_CalcPath( RIFLE, Gov->x, Gov->y, metro->col,
  225.            metro->row, AI5_PATH_BEST);
  226.       InfAccess = Path[0];
  227.       dist = AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row );
  228.       if( InfAccess != -1 ) {
  229.          AI5_CalcPath( ARMOR, Gov->x, Gov->y,
  230.             metro->col, metro->row, AI5_PATH_BEST);
  231.           ArmorAccess = Path[0];
  232.          LastReq.priority = 40;
  233.          LastReq.type = RIFLE;
  234.          if( (ArmorAccess != -1) && (wishbook[ARMOR].enabled == TRUE) ) {
  235.            if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  236.               Gov->hist.TerrainCounts[HEX_DESERT] +
  237.               //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  238.               Gov->hist.TerrainCounts[HEX_BRUSH])
  239.                        >
  240.               (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  241.               Gov->hist.TerrainCounts[HEX_PEAKS] +
  242.               Gov->hist.TerrainCounts[HEX_SWAMP] +
  243.               Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  244.                   LastReq.type = ARMOR;
  245.          }
  246.       }
  247.      else {  // We can't get there with infantry - maybe Aircav?
  248.        if( (dist <= wishbook[AIRCAV].range) &&
  249.           (wishbook[AIRCAV].enabled == TRUE) ) {
  250.                LastReq.priority = 50;
  251.                LastReq.type = AIRCAV;
  252.         }
  253.         // else no request - too far away to help
  254.       }
  255.       break;
  256.     }
  257.     case GOV_DEFEND:
  258.       // Make ground troops if we are at home, place orders for more if
  259.       //   they can reach.  If not, order up aircraft when in range.
  260.       //   Maybe subs and destroyers too?
  261.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  262.      // If this is my city, let's get some troops
  263.      LastReq.priority = 40;
  264.      LastReq.type = RIFLE;
  265.       }
  266.       else {
  267.       int InfAccess;
  268.          int ArmorAccess;
  269.       long dist = AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row );
  270.         AI5_CalcPath( RIFLE, Gov->x, Gov->y, metro->col,
  271.           metro->row, AI5_PATH_BEST);
  272.         InfAccess = Path[0];
  273.           if( InfAccess != -1 ) {
  274.           AI5_CalcPath( ARMOR, Gov->x, Gov->y,
  275.             metro->col, metro->row, AI5_PATH_BEST);
  276.             ArmorAccess = Path[0];
  277.           LastReq.priority = 30;
  278.           LastReq.type = RIFLE;
  279.           if( (ArmorAccess != -1) && (wishbook[ARMOR].enabled == TRUE) ) {
  280.               if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  281.                 Gov->hist.TerrainCounts[HEX_DESERT] +
  282.                 //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  283.                 Gov->hist.TerrainCounts[HEX_BRUSH])
  284.                       >
  285.                (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  286.                 Gov->hist.TerrainCounts[HEX_PEAKS] +
  287.                 Gov->hist.TerrainCounts[HEX_SWAMP] +
  288.                 Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  289.                   LastReq.type = ARMOR;
  290.              }
  291.         }
  292.       else {
  293.           if(dist <= wishbook[FIGHTER].range) {
  294.               LastReq.priority = 30;
  295.              LastReq.type = AI5_select_from_prefs
  296.                  ( &AI5_CITY_DEF_AIR, metro->unit_type );
  297.           }
  298.           else {
  299.               if( (type == GOV_ISLAND) || (type == GOV_PORT) ) {
  300.                  // They can build ships too
  301.                   LastReq.type = AI5_select_from_prefs
  302.                       (&AI5_CITY_DEF_SEA, metro->unit_type);
  303.               }
  304.               // Else forget it.
  305.            }
  306.        }
  307.       } // End else this is not my city
  308.       break;
  309.     case GOV_SEARCH:
  310.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  311.      // Only make a request if this is my city
  312.      if( Gov->hist.TotalMyUnits < 1 )  {
  313.          // Make an aircraft first off, then make ground forces.
  314.          LastReq.priority = 20;
  315.          LastReq.type = AI5_select_from_prefs
  316.       (&AI5_CITY_SEARCH_FIRST, metro->unit_type);
  317.      }
  318.      else {
  319.        // Make ground forces
  320.        LastReq.priority = 15;
  321.        LastReq.type = ARMOR;
  322.        if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  323.       Gov->hist.TerrainCounts[HEX_DESERT] +
  324.       //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  325.       Gov->hist.TerrainCounts[HEX_BRUSH])
  326.                   <
  327.           (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  328.       Gov->hist.TerrainCounts[HEX_PEAKS] +
  329.       Gov->hist.TerrainCounts[HEX_SWAMP] +
  330.       Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  331.                   LastReq.type = RIFLE;
  332.      }
  333.       }
  334.       break;
  335.     case GOV_SUPPORT:
  336.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  337.      // Only make a request if this is my city
  338.      // Make other stuff - Transports, aircraft, destryers + subs, etc.
  339.           //  depending on whether the city is a PORT or not
  340.      LastReq.priority = 1;  // Make the request very low priority so
  341.      // it gets overridden easily by more needy Governors.
  342.      if( type == GOV_PORT ) {
  343.          LastReq.type = AI5_select_from_prefs
  344.       ( &AI5_CITY_SUPPORT_SEA, metro->unit_type );
  345.      }
  346.      else {
  347.          LastReq.type = AI5_select_from_prefs
  348.       ( &AI5_CITY_SUPPORT_LAND, metro->unit_type );
  349.      }
  350.       }
  351.       break;
  352.     } // End switch statement
  353. }
  354.  
  355.  
  356. void  AI5_get_island_req( struct GovNode* Gov, struct City* metro,
  357.       enum GovType type )
  358. {
  359.     switch (Gov->mode) {
  360.     case GOV_TAKEN:
  361.       // Hoo-boy we are in trouble now!  Can we find a city close enough
  362.       //    to send Aircav?
  363.       if( wishbook[AIRCAV].enabled == TRUE ) {
  364.      if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <=
  365.          wishbook[AIRCAV].range ) {
  366.          LastReq.type = AIRCAV;
  367.          LastReq.priority = 50;
  368.      }
  369.       }
  370.       break;
  371.     case GOV_DEFEND:
  372.       // Lets make some aircraft to defend with if this is our city,
  373.       //   else, order aircraft if close enough, else order ships.
  374.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  375.      LastReq.priority = 30;
  376.      LastReq.type = AI5_select_from_prefs
  377.        (&AI5_ISLE_DEF_OWN, metro->unit_type);
  378.       }
  379.       else {
  380.      if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <=
  381.          wishbook[FIGHTER].range ) {
  382.          LastReq.type = AI5_select_from_prefs
  383.       (&AI5_ISLE_DEF_AIR, metro->unit_type);
  384.          LastReq.priority = 40;
  385.      }
  386.      else {
  387.          if( type == GOV_PORT ) {
  388.         LastReq.priority = 30;
  389.         LastReq.type = AI5_select_from_prefs
  390.           (&AI5_ISLE_DEF_SEA, metro->unit_type);
  391.          }
  392.      }
  393.       }
  394.       break;
  395.     case GOV_SEARCH:
  396.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  397.      if( Gov->hist.TotalMyUnits < 1 )  {
  398.          // Make an aircraft first off, then make AIRCAV if we can.
  399.          LastReq.priority = 20;
  400.          LastReq.type = AI5_select_from_prefs
  401.       (&AI5_ISLE_SEARCH_FIRST, metro->unit_type);
  402.      }
  403.      else {
  404.          LastReq.priority = 15;
  405.          LastReq.type = AI5_select_from_prefs
  406.       (&AI5_ISLE_SEARCH_OWN, metro->unit_type);
  407.      }
  408.       }
  409.       else {
  410.      // Ony ask for something if we are close enough to get it.
  411.      if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <=
  412.          wishbook[FIGHTER].range ) {
  413.          LastReq.priority = 15;
  414.          LastReq.type = AI5_select_from_prefs
  415.       (&AI5_ISLE_SEARCH_AIR, metro->unit_type);
  416.      }
  417.       }
  418.       break;
  419.     case GOV_SUPPORT:
  420.       if( (Gov->x == metro->col) && (Gov->y == metro->row) ) {
  421.      // Only make a request if this is our city - makes sure we do
  422.      //    something even if no one esle needs anything
  423.      LastReq.priority = 1;  // of course, make it low pri so others
  424.                             // override it easily.
  425.      LastReq.type = AI5_select_from_prefs
  426.        (&AI5_ISLE_SUPPORT, metro->unit_type);
  427.       }
  428.       break;
  429.     }
  430. }
  431.  
  432.  
  433. void  AI5_get_transport_req( struct GovNode* Gov, struct City* metro,
  434.       enum GovType type )
  435. {
  436.     switch (Gov->mode) {
  437.     case GOV_ATTACK:
  438.       // We need troops
  439.       if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <= 25 ) {
  440.      LastReq.priority = 30;
  441.      LastReq.type = AI5_select_from_prefs
  442.        (&AI5_TRANS_ATTACK, metro->unit_type);
  443.       }
  444.       break;
  445.     case GOV_DEFEND:
  446.       // We need escorts
  447.       if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <= 25 ) {
  448.      if( (type == GOV_PORT) || (type == GOV_ISLAND) ) {
  449.          LastReq.priority = 30;
  450.          LastReq.type = AI5_select_from_prefs
  451.       (&AI5_TRANS_DEFEND, metro->unit_type);
  452.      }
  453.       }
  454.       break;
  455.     case GOV_OUTNUMBERED:
  456.       // No request - we're on the run
  457.       break;
  458.     }
  459. }
  460.  
  461.  
  462. void  AI5_get_carrier_req( struct GovNode* Gov, struct City* metro,
  463.       enum GovType type )
  464. {
  465.     switch (Gov->mode) {
  466.     case GOV_ATTACK:
  467.     case GOV_DEFEND:
  468.       if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <= 25 ) {
  469.      // Order up some more Destroyers for escorts, more planes, etc.
  470.      if( (type == GOV_PORT) || (type == GOV_ISLAND) ) {
  471.          LastReq.priority = 30;
  472.          LastReq.type = AI5_select_from_prefs
  473.       (&AI5_CARR_ATT_SEA, metro->unit_type);
  474.      }
  475.      else {
  476.          LastReq.priority = 30;
  477.          LastReq.type = AI5_select_from_prefs
  478.       (&AI5_CARR_ATT_AIR, metro->unit_type);
  479.      }
  480.       }
  481.       break;
  482.     case GOV_OUTNUMBERED:
  483.       // No request - we're on the run
  484.       break;
  485.     }
  486. }
  487.  
  488.  
  489. void  AI5_get_battleship_req( struct GovNode* Gov, struct City* metro,
  490.       enum GovType type )
  491. {
  492.     switch (Gov->mode) {
  493.     case GOV_ATTACK:
  494.     case GOV_DEFEND:
  495.       // Is the city in range and a PORT or ISLAND?
  496.       if( (type == GOV_PORT) || (type == GOV_ISLAND) ) {
  497.      if( AI5_GetDist( Gov->x, Gov->y, metro->col, metro->row ) <= 25 ) {
  498.          // Order up some more Destroyers for escorts
  499.          LastReq.priority = 30;
  500.          LastReq.type = AI5_select_from_prefs
  501.         (&AI5_BATT_ATTACK, metro->unit_type);
  502.      }
  503.       }
  504.       break;
  505.     case GOV_OUTNUMBERED:
  506.       // No request - we're on the run
  507.       break;
  508.     }
  509. }
  510.  
  511.  
  512. int AI5_select_from_prefs( struct GovPrefs* prefs, short CurrProd )
  513. {
  514.     short     rand;
  515.     int       i;
  516.  
  517.     rand = (short) (RangeRand(100L) + 1); // 1 to 100
  518.  
  519.     for (i=0; i < MAX_PREFS; i++) {
  520.         // We have to make sure we can make this type of unit
  521.         if (wishbook[prefs->type[i]].enabled == TRUE) {
  522.             rand -= prefs->priority[i];
  523.             if (CurrProd == prefs->type[i])
  524.            rand -= CURRENT_PRODUCT_BONUS;
  525.             if (rand <= 0) return (prefs->type[i]);
  526.         }
  527.         // Else, skip over - we can't build one in this game
  528.     }
  529.  
  530.     DEBUG_AI5("Preferences Selection fell through - taking first one")
  531.     return (prefs->type[0]);
  532. }
  533.  
  534. /***************************************************************
  535. *************** Path Finding Routines  *************************
  536. ***************************************************************/
  537. #define REVERSE_DIRECTION(num)  ((num+3)%6)
  538. void  AI5_CalcPath (short type, short orgx, short orgy, short destx,
  539.       short desty, int rigor)
  540. {
  541.   int                i;
  542.   short              targx, targy;
  543.   int                Pickdir;
  544.   long               NewCost;
  545.   struct PathNode*   PNode = NULL;
  546.   struct PathNode*   PNode2 = NULL;
  547.   struct PathNode*   WalkNode;
  548.   int                Found;
  549.   // First, we initialize some stuff and check for input errors
  550.   // Set up the globals
  551.   Type = type;  // One less push and pop on the stack for getting cost
  552.   //PathDiv = rigor;  // Dial up the precision required - 1 to 10
  553.   if (rigor < 1) rigor = 1;
  554.   if (rigor > 10) rigor = 10;
  555.   Destx = destx;
  556.   Desty = desty;
  557.   // Clear the return values
  558.   for( i=0; i<MAX_PATH; i++ )  Path[i] = -1;
  559.   // Check for bad inputs
  560.   if( (orgx >= width)||(orgx < 0)||(orgy >= height)||(orgy < 0) ) return;
  561.   if( (destx >= width)||(destx < 0)||(desty >= height)||(desty < 0) ) return;
  562.   // Now, check the direction map
  563.   if( !PathMap ) {
  564.     // Must be the first time this has been called
  565.     // Allocate PathMap, set PathMapX and PathMapY, initialize the
  566.     //    lists.
  567.     PathMap = (int*) malloc( sizeof(int) * width * height );
  568.     if( !PathMap ) return;  // Abort this!
  569.     PathMapX = width;
  570.     PathMapY = height;
  571.     for(i=0; i< (width*height); i++)  PathMap[i] = -1;
  572.   }
  573.   if( (PathMapX != width) || (PathMapY != height) ) {
  574.     // We have started a new game without exitting - the map changed
  575.     PathMap = (int*) malloc( sizeof(int) * width * height );
  576.     if( !PathMap ) return;  // Abort this!
  577.     PathMapX = width;
  578.     PathMapY = height;
  579.     for(i=0; i< (width*height); i++)  PathMap[i] = -1;
  580.   }
  581.   // Initialize both lists, just to be sure we're starting good.
  582.   NewList ((struct List*)&OpenList);
  583.   NewList ((struct List*)&DoneList);
  584.  
  585.    // To begin, insert the starting node into the OpenList
  586.   PNode = AllocVec( (long)sizeof(*PNode ), MEMF_CLEAR );
  587.   if( !PNode ) return; // Abort!  Can't get memory!
  588.   // We will start from the destination, so recording the path after
  589.   //   will be easier.
  590.   PNode->x = destx;
  591.   PNode->y = desty;
  592.   PNode->cost = 0;
  593.   // Here we use the rigor to define how well to search for paths
  594.   // Underestimating is best - makes us check more possible paths
  595.   //   but it is slower.  Rigor goes from 1 to 10, with 1 being the
  596.   //   most rigorous and 10 the least (it will settle on less optimal
  597.   //   paths).
  598.   PNode->eta = AI5_GetDist( orgx, orgy, destx, desty ) * 10 * rigor;
  599.   AddHead((struct List *)&OpenList,(struct Node *)PNode);
  600.   // Now, enter the while loop until we are done
  601.   while( !emptylistP( &OpenList ) ) {
  602.     // Change - make a new routine to get the best bet out of OpenList
  603.     PNode = AI5_OpenListGetBest();
  604.     // Put it in the DoneList - we are visiting it now
  605.     AddTail((struct List*)&DoneList,(struct Node*)PNode);
  606.     // Check for destination
  607.     if( (PNode->x == orgx) && (PNode->y == orgy) )  break;
  608.     // Not there yet, so let's look at the hexes around it
  609.     // Pick a random direction to start with
  610.     Pickdir = RangeRand( 6L ); // Gets us 0-5
  611.     i = Pickdir; // To start us off on Pickdir
  612.     do {
  613.       // Get the hex in that direction
  614.       if( AI1_calc_dir (i, PNode->x, PNode->y, &targx, &targy) != -1 ) {
  615.        // Find the cost to get to the new hex
  616.        NewCost = AI5_GetCost( PNode->x, PNode->y);
  617.        // Can we even go here?
  618.        if( NewCost < 0 ) {
  619.           i++;
  620.           if( i > 5 ) i=0;
  621.           continue;
  622.         }
  623.  
  624.        PNode2 = AllocVec( (long)sizeof(*PNode2 ), MEMF_CLEAR );
  625.        // On memory problem, get out
  626.        if( !PNode2 ) {
  627.           i++;
  628.           if( i > 5 ) i=0;
  629.           continue;
  630.         }
  631.        PNode2->x = targx;
  632.        PNode2->y = targy;
  633.        PNode2->cost = PNode->cost + NewCost;
  634.        // We use the *60 here to estimate that we will spend 60/hex
  635.        PNode2->eta = AI5_GetDist( targx, targy, orgx, orgy )*10*rigor;
  636.  
  637.        // If not marked on the map already, mark it and place it in OpenList
  638.        if( PathMap[ targy * width + targx ] == -1 ) {
  639.          PathMap[ targy * width + targx ] = REVERSE_DIRECTION(i);
  640.          AddTail( (struct List*)&OpenList, (struct Node*)PNode2 );
  641.        }
  642.        // else, try to find it in the OpenList, and replace it ifs cost
  643.        //    is greater (took more to get there) and mark it again.
  644.        else {
  645.          Found = FALSE;
  646.          WalkNode = (struct PathNode*)OpenList.mlh_Head;
  647.          for(; WalkNode->pnode.mln_Succ; WalkNode =
  648.           (struct PathNode*)WalkNode->pnode.mln_Succ) {
  649.            if( (WalkNode->x == PNode2->x) && (WalkNode->y == PNode2->y) ) {
  650.              // found the matching node
  651.              if( WalkNode->cost > PNode2->cost ) {
  652.               // replace it - classic extraction from a double linked list
  653.              Remove( (struct Node*)WalkNode );
  654.               FreeVec( WalkNode );
  655.          //WalkNode = NULL; // Just to make sure
  656.               Found = TRUE;
  657.               AddTail( (struct List*)&OpenList, (struct Node*)PNode2 );
  658.               // Mark the spot
  659.              PathMap[ targy * width + targx ] = REVERSE_DIRECTION(i);
  660.           goto AI5_CALCPATH_FOUND_MATCH;
  661.              }
  662.            }
  663.          } // End for loop
  664.          // if we didn't find it, forget it.
  665. AI5_CALCPATH_FOUND_MATCH:
  666.          if( Found == FALSE ) FreeVec( PNode2 );
  667.           PNode2 = NULL; // Just to make sure.
  668.        } // End else was already marked
  669.       } // End if there is a hex in this direction
  670.       // increment our direction
  671.       i++;
  672.       if( i > 5 ) i=0;
  673.     } while( i != Pickdir); // End do-while loop of directions
  674.   } // End while OpenList not empty
  675.  
  676.   PathLength = 0;
  677.   PathCost = -1;
  678.   // OK, now check that we have a path
  679.   if( (PNode->x == orgx) && (PNode->y == orgy) &&
  680.     (PNode->cost > 0) && (PNode->eta == 0) ) {
  681.     // we found a path!
  682.     // Record it.
  683.     targx = orgx;
  684.     targy = orgy;
  685.     // And record the total cost of moving along the path
  686.     PathCost = (int)PNode->cost;
  687.     for(i=0; i<MAX_PATH; i++) {
  688.       // Record the first steps
  689.       Path[i] = PathMap[ targy * width + targx ];
  690.       PathLength++;
  691.       // Look!  This routine even works on its own parameters!
  692.       if( AI1_calc_dir (Path[i], targx, targy, &targx, &targy) == -1 ) {
  693.         break;
  694.       }
  695.       if( (targx == destx) && (targy == desty) ) {
  696.         // we made it! exit!
  697.         break;
  698.       }
  699.     }
  700.   }
  701.   // else nothing - we already cleared the path to nothing
  702.  
  703.   // Erase the marked up map for the next person
  704.   for( WalkNode = (struct PathNode*)OpenList.mlh_Head; WalkNode->pnode.mln_Succ;
  705.        WalkNode = (struct PathNode*)WalkNode->pnode.mln_Succ ) {
  706.     PathMap[ WalkNode->y * width + WalkNode->x ] = -1;
  707.   }
  708.   // For debugging / timing count the nodes here - easier to see
  709.   i = count_nodes( &OpenList );
  710.   for( WalkNode = (struct PathNode*)DoneList.mlh_Head; WalkNode->pnode.mln_Succ;
  711.        WalkNode = (struct PathNode*)WalkNode->pnode.mln_Succ ) {
  712.     PathMap[ WalkNode->y * width + WalkNode->x ] = -1;
  713.   }
  714.  
  715.   // Cleanup both lists
  716.   nuke_list( &OpenList );
  717.   nuke_list( &DoneList );
  718.  
  719.   // And, finally, we check for aircraft, and verify that we have enough
  720.   //   fuel to reach the destination with a full tank.  User will have to
  721.   //   check the path length vs. current fuel - this has to be able to do
  722.   //   what-if calculations so we can't ask for a unit.
  723.   if( (wishbook[Type].range > 0) && (wishbook[Type].range < PathLength ) ) {
  724.       // Not enough fuel! Clear the path we got.
  725.       PathLength = 0;
  726.       for( i=0; i<MAX_PATH; i++ )  Path[i] = -1;
  727.   }
  728.   return;
  729. }
  730.  
  731. // The deterrance factor to try and avoid stacking units when there is no
  732. //   need (equal paths otherwise).  Try to avoid dangerous stacks of
  733. //   units that are prime enemy targets.  Increase this later if it does
  734. //   not work well enough. Started with 30 - increased to 60, maybe that
  735. //   will do it.
  736. #define STACKING_COST 60
  737. long  AI5_GetCost( short orgx, short orgy)
  738. {
  739.   // Returns the cost to move into that hex, or -1 if it is not possible
  740.   struct MapIcon*  icon = NULL;
  741.  
  742.   long cum_cost = movement_cost_table[Type][get(PLAYER.map, orgx, orgy)];
  743.   if( (Type == RIFLE) || (Type == ARMOR) ) {
  744.     // Check for roads
  745.     if( get_flags(PLAYER.map, orgx, orgy)&ROAD ) cum_cost = 30;
  746.   }
  747.  
  748.   // Check for units - friendly ones we want to try to avoid (less of
  749.   //    a target if not stacked), or will have to if no stacking
  750.   //    allowed.  Enemy units are always avoided (or should that be
  751.   //    ignored?).  Ignored I guess - blast through if you have to!
  752.   //    Changed! Now we will use the same deterence factor to try and
  753.   //    avoid enemy units as well - we should, all things being equal.
  754.   // Search through the player's icons for one located here
  755.   // Lets do some more here - start with the cost for the terrain itself,
  756.   //    then add for each icon found at that location - so that stacks
  757.   //    of units are accounted for.
  758.   for(icon=(struct MapIcon *)PLAYER.icons.mlh_Head;icon->inode.mln_Succ;
  759.     icon = (struct MapIcon *)icon->inode.mln_Succ ) {
  760.        if( (icon->col == orgx) && (icon->row == orgy) ) {
  761.       if( icon->owner != player ) {
  762.                // We assume that the player wants to avoid enemy cities
  763.                //  otherwise, he would have headed for it directly.
  764.                if( icon->type == CITY )  {
  765.                   // Here's a little extra - an enemy city at the destination
  766.                   // is OK - since we are specifically trying to get there.
  767.                   if( (icon->col == Destx) && (icon->row == Desty) )
  768.                     return (10);
  769.                   else
  770.                     return -1;
  771.                }
  772.           // else we have an enemy unit at that location.
  773.           // Need to add a proviso here - don't give a positive cost
  774.           // if we started with a negative one
  775.                if( cum_cost > 0 ) cum_cost += STACKING_COST;
  776.       }
  777.       else {
  778.                // Only costs 10 to enter a city - does end one's turn though.
  779.                if( icon->type == CITY )  {
  780.          // We want to reach a city at the destination always.
  781.          if( (icon->col == Destx) && (icon->row == Desty) ) {
  782.             return( 10 );
  783.          }
  784.          // Else, may not be at the destination.  So, try to avoid
  785.          //  the city unless we waste a lot going around.  Only
  786.          //  for aircraft types though, that can make lots of
  787.          //  moves per turn (include destroyers too they move 3).
  788.          if( (Type == DESTROYER) || (Type == FIGHTER) ||
  789.              (Type == BOMBER) || (Type == AIRCAV) ) {
  790.              return (120);
  791.          }
  792.          // Else
  793.          return 10;
  794.           }
  795.           // Else, must be a unit there...
  796.           if( !opt.stacking )  return( -1 );
  797.           // else, add the stacking cost if we can move there
  798.                if( cum_cost > 0 ) {
  799.          cum_cost += STACKING_COST;
  800.           }
  801.           else {
  802.          // We need to handle special cases here - we may be
  803.          //  sending a ground unit to board a ship which is on
  804.          //  water - we have to allow this.
  805.          if( ((Type == ARMOR) || (Type == RIFLE) ) &&
  806.              (icon->type == TRANSPORT) &&
  807.              ((icon->col == Destx) && (icon->row == Desty)) ) {
  808.              // We have a ground unit trying to get onto a
  809.              //  transport.  So let him.
  810.              return (10);
  811.          }
  812.           }
  813.       } // End else icon is player's
  814.    } // End if icon in this hex
  815.   } // End for loop
  816.   return( cum_cost );
  817. }
  818.  
  819.  
  820. #define Floor2(x) (((x) >= 0) ? ((x)>>1) : (((x)-1)>>1))
  821. #define Ceil2(x) (((x) >= 0) ? (((x)+1)>>1) : ((x)>>1))
  822. #define Sign(x) (((x) >= 0) ? (1) : (-1) )
  823. // This next routine calculates distances between hexes.
  824. long  AI5_GetDist( short orgx, short orgy, short destx, short desty)
  825. {
  826.   // Here's Amit's code for getting distance in a hexagonal grid laid
  827.   //   out in a rectangular matrix - short and sweet.
  828.   int x1 = orgx - Floor2(orgy);
  829.   int y1 = orgx + Ceil2(orgy);
  830.   int x2 = destx - Floor2(desty);
  831.   int y2 = destx + Ceil2(desty);
  832.   int dx = x2 - x1;
  833.   int dy = y2 - y1;
  834.   long dist, dist2, dest3;
  835.  
  836.   if( Sign(dx) == Sign(dy) ) {
  837.     dist = (long) max( abs(dx),abs(dy));
  838.   }
  839.   else {
  840.     dist = (long) abs(dx) + abs(dy);
  841.   }
  842.   if( !wrap ) return dist;
  843.  
  844.   // Now the code to handle the case of wrapping - this isn't going to
  845.   //   be pretty
  846.   // I'm doing it by taking the distance from the nearest edge (left or
  847.   //   right, top or bottom) to get the separation between the two
  848.   //   points in question, then calculate as normal.
  849.  
  850.   // Well, maybe not.  Let's see if we can do this by committee, by
  851.   //   taking the best of three - the original one, a second done by
  852.   //   adding width and height to the first coords, and a third done
  853.   //   by adding width and height to the second coords.  I think the
  854.   //   least of the three will be the best dist, and the lesser of
  855.   //   the other two is the wrap distance.
  856. }
  857.  
  858.  
  859. struct PathNode* AI5_OpenListGetBest()
  860. {
  861.   // Note: This routine assumes that the OpenList is not empty - which
  862.   //   should be valid when this is called.  This can be made "inline"
  863.   //   in the AI5_CalcPath routine to speed us up a trifle.
  864.   struct PathNode* WalkNode = (struct PathNode*)OpenList.mlh_Head;
  865.   struct PathNode* Best = WalkNode;
  866.  
  867.   for( ; WalkNode->pnode.mln_Succ; WalkNode =
  868.     (struct PathNode*)WalkNode->pnode.mln_Succ) {
  869.     if( (WalkNode) && ((WalkNode->cost+WalkNode->eta)<(Best->cost+Best->eta)))
  870.       Best = WalkNode;
  871.   }
  872.   // No need to give a list here - it doesn't need to know
  873.   Remove( (struct Node*) Best );
  874.   return Best;
  875. }
  876.  
  877. /***************************************************************
  878. *************** Action Routines  *******************************
  879. ***************************************************************/
  880. void  AI5_set_gov_mode( struct GovNode* Gov )
  881. {
  882.   enum GovMode new_mode;
  883.   struct Unit* unit;
  884.  
  885.   switch (Gov->type) {
  886.   case GOV_CITY:
  887.   case GOV_PORT:
  888.   case GOV_ISLAND:
  889.     // Start out with the most hopeful
  890.     new_mode = GOV_SUPPORT;
  891.  
  892.     // If we don't have many units, stay in Search
  893.     if(Gov->hist.TotalMyUnits < 6)
  894.       new_mode = GOV_SEARCH;
  895.  
  896.     // Now, if we have enemy forces in the area, set to defend
  897.     if(( Gov->hist.TotalEUnits > 0 ) || (Gov->hist.EnemyCounts[CITY] > 0) )
  898.          new_mode = GOV_DEFEND;
  899.  
  900.     // Real bad news - if we don't even have our city, set TAKEN
  901.     if ((Gov->type == GOV_CITY) || (Gov->type == GOV_PORT)
  902.    || (Gov->type == GOV_ISLAND))
  903.       /* Check to see if we still own our city */
  904.       if (hex_owner(Gov->x, Gov->y) != player) new_mode = GOV_TAKEN;
  905.  
  906.     break;
  907.   case GOV_TRANSPORT:
  908.     // Start out most hopeful
  909.     new_mode = GOV_ATTACK;
  910.     if( Gov->hist.TotalEUnits > 0 )
  911.       new_mode = GOV_DEFEND;
  912.     if( Gov->hist.TotalEUnits > 5 )
  913.       new_mode = GOV_OUTNUMBERED;
  914.     unit = AI5_LocateUnit( Gov );
  915.     if( (unit) && (unit->damage > 0) )
  916.       new_mode = GOV_OUTNUMBERED;
  917.     break;
  918.   case GOV_CARRIER:
  919.     // Start out most hopeful
  920.     new_mode = GOV_ATTACK;
  921.     if( Gov->hist.TotalEUnits > Gov->hist.TotalMyUnits )
  922.       new_mode = GOV_DEFEND;
  923.     if( Gov->hist.TotalEUnits > 2 * Gov->hist.TotalMyUnits )
  924.       new_mode = GOV_OUTNUMBERED;
  925.     unit = AI5_LocateUnit( Gov );
  926.     if( (unit) && (unit->damage >= wishbook[unit->type].hitpoints / 3) )
  927.       new_mode = GOV_OUTNUMBERED;
  928.     break;
  929.   case GOV_BATTLESHIP:
  930.     // Start out most hopeful
  931.     new_mode = GOV_ATTACK;
  932.     if( Gov->hist.TotalEUnits > 5 )
  933.       new_mode = GOV_OUTNUMBERED;
  934.     unit = AI5_LocateUnit( Gov );
  935.     if( (unit) && (unit->damage >= 2 * wishbook[unit->type].hitpoints / 3) )
  936.       new_mode = GOV_OUTNUMBERED;
  937.     break;
  938.   } // End switch Gov->type
  939.  
  940.   // If we change modes, have all units await new orders
  941.   if( new_mode != Gov->mode ) {
  942.     Gov->mode = new_mode;
  943.     // clear all orders
  944.     AI1_clear_all_orders( Gov );
  945.   }
  946.   return;
  947. }
  948.  
  949. struct Unit* AI5_LocateUnit( struct GovNode *Gov )
  950. {
  951.     int GovID, UnitID;
  952.     struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  953.     for (;unit->unode.mln_Succ;unit=(struct Unit*)unit->unode.mln_Succ) {
  954.         // Check for the Main unit for this Governor
  955.         if( (unit->owner == player) && (unit->name) ) {
  956.        if( scanf( unit->name, "%ld %ld", &GovID, &UnitID ) >= 1 ) {
  957.          if( GovID == Gov->ID ) {
  958.       if((Gov->type == GOV_TRANSPORT)&&(unit->type == TRANSPORT)) {
  959.         // we found it!
  960.         return (unit);
  961.       }
  962.       if((Gov->type == GOV_CARRIER)&&(unit->type == CARRIER)) {
  963.         // we found it!
  964.         return (unit);
  965.       }
  966.       if((Gov->type == GOV_BATTLESHIP)&&(unit->type == BATTLESHIP)) {
  967.         // we found it!
  968.         return (unit);
  969.       }
  970.          }
  971.        } // End if scanf worked
  972.    }
  973.     }      // End for loop
  974.     return NULL;
  975. }
  976.  
  977.  
  978. void  AI5_play_turn( int new_units )
  979. {
  980.     int   MaxLooping = 5000;
  981.     /* Here we may want to look around, give out some orders to units,
  982.        and execute orders to units in a loop until we have done all the
  983.        moves possible for the units.
  984.        */
  985.     AI5_do_all_histograms();
  986.     AI5_give_orders();
  987.     /* We'll add a little failsafe so we don't spend eternity here */
  988.     while( (MaxLooping > 0) && (DoUnitActions(40,60)) )  MaxLooping--;
  989.     if( MaxLooping <= 0 )
  990.        DEBUG_AI5("Error: Exitting AI player's turn - out of actions!")
  991.     return;
  992. }
  993.  
  994.  
  995. void  AI5_do_all_histograms()
  996. {
  997.     struct GovNode *Gov = (struct GovNode *)GovList.mlh_Head;
  998.     /* First we update the picture for all the Governors */
  999.     for ( ; Gov->gnode.mln_Succ; Gov = (struct GovNode *)Gov->gnode.mln_Succ)
  1000.    if (Gov->owner == player) {
  1001.        AI3_setup_area_of_interest( Gov, 6, 20 );
  1002.        AI1_do_one_histogram (Gov);
  1003.        AI5_set_gov_mode (Gov);
  1004.    }
  1005.     /* End for loop */
  1006. }
  1007.  
  1008.  
  1009. int  AI5_do_unit_actions()
  1010. {
  1011.         return 0;
  1012. }
  1013.  
  1014.  
  1015. /***************************************************************
  1016. *************** Reaction Routines  *****************************
  1017. ***************************************************************/
  1018. int  AI5_recommend_action( struct Unit* unit, int OddsLimit, int LowOdds )
  1019. {
  1020.     /* This routine will evaluate a unit's surroundings and recommend
  1021.        a course of action - or no course of action. A "course of action"
  1022.        is quite literally that - what direction to move the unit in.
  1023.  
  1024.        What it will do is check all six hexes around the unit in question
  1025.        - looking for enemy units, cities, etc - then look at all six
  1026.        results and make the choice of what to do. Or do nothing if there
  1027.        are no enemy units or cities around.
  1028.  
  1029.        The OddsLimit is the minimum odds a player will want to go ahead
  1030.        with (i.e. to play it very safe a player may go with 60 or 70, and
  1031.        to be daring go with 30 or 40) - odds that he will win.
  1032.  
  1033.        LowOdds are the lowest odds we will go with to attack a low value
  1034.        target (i.e. to be daring is to pick 50 or 60, to be safe is to
  1035.        pick 75 or 80 or so), that is, attack an Infantry with a Battleship.
  1036.        The Battleship will win almost every time, but the Infantry has a
  1037.        finite, if very small, chance of winning as well.
  1038.        */
  1039.     int                i;
  1040.     short              targx, targy;
  1041.     struct  HexReport  neighbors[6];
  1042.     int                GainDiff = 0;
  1043.     int                BestDiff = 0;
  1044.     int                BestOdds = 0;
  1045.     int                InDanger = FALSE;
  1046.     int                Recommend = -1;  // None to start with
  1047.  
  1048.  
  1049.     /* Make sure the limits are OK */
  1050.     if( OddsLimit > 100 ) OddsLimit = 100;
  1051.     if( LowOdds < 0 )  LowOdds = 0;
  1052.  
  1053.     for( i=0; i<6; i++) {
  1054.    /* Fill in the raw data */
  1055.          if (AI1_calc_dir (i, unit->col, unit->row, &targx, &targy) != -1) {
  1056.            AI5_evaluate_hex( unit, targx, targy, &neighbors[i] );
  1057.        }
  1058.     }
  1059.  
  1060.     /* Next, make sense of the six chunks of data */
  1061.     for( i=0; i < 6; i++ ) {
  1062.         // Can they attack us?
  1063.         if (neighbors[i].AtRisk)  InDanger = TRUE;
  1064.         if( neighbors[i].CanAttack ) {
  1065.        //  We might want to go with this one
  1066.        GainDiff = neighbors[i].Gain - neighbors[i].Risk;
  1067.        if( neighbors[i].Odds < OddsLimit )
  1068.            continue;  /* Too heavy odds */
  1069.        if( Recommend == -1 ) {
  1070.            // Go with this one for now if we have a minimal gain
  1071.            if( GainDiff >= 0 ) {
  1072.                Recommend = i;
  1073.               BestDiff = GainDiff;
  1074.                 BestOdds = neighbors[i].Odds;
  1075.           }
  1076.           else {
  1077.               // GainDiff is negative - we are risking more than we
  1078.               //  can gain, so are the odds enough in our favor?
  1079.               if( neighbors[i].Odds > LowOdds ) {
  1080.                   Recommend = i;
  1081.                  BestDiff = GainDiff;
  1082.                     BestOdds = neighbors[i].Odds;
  1083.               }
  1084.           }
  1085.        }
  1086.        else {
  1087.            // Go with this if it is better
  1088.            if( GainDiff > BestDiff ) {
  1089.               BestDiff = GainDiff;
  1090.                 BestOdds = neighbors[i].Odds;
  1091.               Recommend = i;
  1092.            }
  1093.           else {
  1094.                 if( GainDiff == BestDiff ) {
  1095.                     // Check the odds - which one has higher?
  1096.                     if( neighbors[i].Odds > BestOdds ) {
  1097.                         BestDiff = GainDiff;
  1098.                         Recommend = i;
  1099.                         BestOdds = neighbors[i].Odds;
  1100.                     }
  1101.                 }
  1102.               // Maybe it is less risky but still profitable ?
  1103.               else if( (GainDiff > 0) &&
  1104.                  (neighbors[i].Odds > neighbors[Recommend].Odds) ) {
  1105.                   BestDiff = GainDiff;
  1106.                   Recommend = i;
  1107.                     BestOdds = neighbors[i].Odds;
  1108.               } // End elseif GainDiff < BestDiff
  1109.           }
  1110.        } //End else we already have an existing recommendation
  1111.    } // End if can attack
  1112.     }  // end for loop
  1113.     if( Recommend != -1 )  return Recommend;
  1114.  
  1115.     if( (neighbors[5].TotalMyUnits > 1) && (InDanger == TRUE) ) {
  1116.        /* We have multiple units here - we MUST recommend a move
  1117.        as this makes us vulnerable - the enemy WILL attack 'cause
  1118.        they can take out a stack.
  1119.        */
  1120.        /* we need to select the best of the six, if any are valid */
  1121.         for( i=0; i < 6; i++ ) {
  1122.            if( neighbors[i].CanAttack ) {
  1123.                //  We might want to go with this one
  1124.                GainDiff = neighbors[i].Gain - neighbors[i].Risk;
  1125.               if( Recommend == -1 ) {
  1126.                 // Go with this one for now
  1127.                    Recommend = i;
  1128.                 BestDiff = GainDiff;
  1129.                     BestOdds = neighbors[i].Odds;
  1130.             }
  1131.               else {
  1132.                 // Go with this if it is better
  1133.                   if( GainDiff > BestDiff ) {
  1134.                       BestDiff = GainDiff;
  1135.                      Recommend = i;
  1136.                         BestOdds = neighbors[i].Odds;
  1137.                   }
  1138.             } //End else we already have an existing recommendation
  1139.            } // End if can attack
  1140.       }// End for loop
  1141.    // That should give us SOMETHING
  1142.     }
  1143.  
  1144.     // If we STILL don't have anything, but we are in danger,
  1145.     //  then it's time to run!
  1146.     //  This needs to be added later.
  1147.  
  1148.     /* Default to recommending no action */
  1149.     return -1;
  1150. }
  1151.  
  1152.  
  1153. void  AI5_evaluate_hex( struct Unit* unit, short targx, short targy,
  1154.          struct HexReport* rep )
  1155. {
  1156.     /* For enemies, and for friendly units, we will use the unit list
  1157.        and pick out our own so we can get a full picture (with units
  1158.        on board other units, etc).
  1159.        */
  1160.     struct Unit* lookunit = (struct Unit *)unit_list.mlh_Head;
  1161.     struct City* metro = city_hereP (unit->col, unit->row);
  1162.     struct City* enemy_city = city_hereP (targx, targy);
  1163.     struct Unit* enemy = NULL;
  1164.  
  1165.     // Zero out all the fields in the report we are to ship back
  1166.     rep->TotalEUnits = 0;
  1167.     rep->TotalMyUnits = 0;
  1168.     rep->AtRisk = FALSE;
  1169.     rep->CanAttack = TRUE;
  1170.     rep->Gain = 0;
  1171.     rep->Risk = 0;
  1172.     rep->Odds = -1;
  1173.  
  1174.     // We need to check that the possible "enemy_city" is in fact an
  1175.     //    enemy city.
  1176.     if ((enemy_city)&&(enemy_city->owner==player))  enemy_city=NULL;
  1177.  
  1178.     for ( ;lookunit->unode.mln_Succ; lookunit = (struct Unit*)
  1179.       lookunit->unode.mln_Succ) {
  1180.        if(( lookunit->owner == player ) && (lookunit->col == unit->col ) &&
  1181.          ( lookunit->row == unit->row ) ) {
  1182.            // We have identified our units, but this will only have meaning
  1183.            //   if the enemy can attack us.  We use the unit list here instead
  1184.            //   of icons since we want to be sure we account for all our
  1185.            //   units, and loaded units or units in cities don't show as icons.
  1186.            rep->TotalMyUnits++;
  1187.            rep->Risk += AI5_unit_value( lookunit->type );
  1188.        } /* End if own unit in right spot */
  1189.        if(( lookunit->owner != player ) && (lookunit->col == targx) &&
  1190.          (lookunit->row == targy) && (lookunit->ship == NULL) &&
  1191.           (enemy_city == NULL) ) {
  1192.            //   Note: Exclude enemy units on ships - we 'can't see them'.
  1193.             //   Note2: Exclude enemy units in cities - we can't see them
  1194.             //      either.
  1195.            rep->TotalEUnits++;
  1196.            // can we attack? Assume we can, and cancel it if we cannot.
  1197.            if( !AI5_can_attack( unit->type, lookunit->type, targx, targy ) ) {
  1198.                rep->CanAttack = FALSE;
  1199.            }
  1200.            // can they attack us?
  1201.            if(AI5_can_attack(lookunit->type,unit->type,unit->col,unit->row)) {
  1202.                rep->AtRisk = TRUE;
  1203.            }
  1204.             else {
  1205.                 // Check for a city
  1206.                 if( (metro) &&
  1207.                   (AI5_can_attack(lookunit->type,CITY,unit->col,unit->row)) ) {
  1208.                         rep->AtRisk = TRUE;
  1209.                 }
  1210.             }
  1211.            // What's the gain?
  1212.            rep->Gain += AI5_unit_value( lookunit->type );
  1213.        } // End if enemy unit in right spot
  1214.     } /* End for loop */
  1215.     // OK, now we handle the case where we didn't have any enemy units around
  1216.     if( rep->TotalEUnits <= 0 ) {
  1217.         rep->CanAttack = FALSE;
  1218.     }
  1219.     // If we have a city at our location, add that into the mix
  1220.     if( metro ) {
  1221.         rep->Risk += AI5_unit_value( CITY );
  1222.     }
  1223.     // And if there is an enemy city at the target, add that also
  1224.     if( enemy_city ) {
  1225.         rep->Gain += AI5_unit_value( CITY );
  1226.         if( AI5_can_attack(unit->type, CITY, targx, targy) ) {
  1227.             rep->CanAttack = TRUE;
  1228.             rep->TotalEUnits++;
  1229.         }
  1230.         else {
  1231.             rep->CanAttack = FALSE;
  1232.         }
  1233.     }
  1234.     // Now we figure the odds
  1235.     if( rep->CanAttack ) {
  1236.         if( enemy_city ) {
  1237.             if( enemy_city->owner == 0 ) {
  1238.                 // Neutral cities don't play by the rules, so I have
  1239.                 //  to wing it here
  1240.                 rep->Odds = AI5_evaluate_odds
  1241.                     ( unit, RIFLE, targx, targy, player );
  1242.             }
  1243.             else {
  1244.                 rep->Odds = AI5_evaluate_odds
  1245.                     ( unit, RIFLE, targx, targy, enemy_city->owner );
  1246.             }
  1247.             if( unit->type == BOMBER ) {
  1248.                 // If we have a bomber, reduce the Gain by to 20% of the
  1249.                 //   city's value - we don't want to encourage bombarding
  1250.                 //   enemy cities to the exclusion of all else.
  1251.                 rep->Gain -= (AI5_unit_value( CITY ) * 0.8);
  1252.             }
  1253.         }
  1254.         else {
  1255.             enemy = choose_defender(unit,targx,targy);
  1256.             if( enemy ) {
  1257.                rep->Odds = AI5_evaluate_odds
  1258.                    ( unit, enemy->type, targx, targy, enemy->owner );
  1259.             }
  1260.         } // End else no enemy city
  1261.     }
  1262. }
  1263.  
  1264.  
  1265. int  AI5_can_attack ( short attacker, short defender, short def_col, short def_row )
  1266. {
  1267.   // This is going to be a long laundry list of unit types, circumstances,
  1268.   //   etc. - mirroring the code in game_play1.c for attacking.
  1269.   short def_terrain = get( PLAYER.map, def_col, def_row );
  1270.   short def_road = get_flags( PLAYER.map, def_col, def_row ) & ROAD;
  1271.   switch( attacker ) {
  1272.         case RIFLE:
  1273.         case ARMOR:
  1274.         case MILITIA:
  1275.             // These three act the same
  1276.             switch( defender ) {
  1277.                 // We can always attack ships (bombardment) unless they
  1278.                 //   have air cover, but we resolve that elsewhere.
  1279.                 case TRANSPORT:
  1280.                 case DESTROYER:
  1281.                 case CRUISER:
  1282.                 case BATTLESHIP:
  1283.                 case CARRIER:
  1284.                     return 1;
  1285.                     break;
  1286.                 case MILITIA:
  1287.                 case ARMOR:
  1288.                 case RIFLE:
  1289.                     // We can always attack other ground troops - armor will
  1290.                     //   just bombard if it can't get there
  1291.                     return 1;
  1292.                     break;
  1293.                 case CITY:
  1294.                 case AIRBASE:
  1295.                     return 1;
  1296.                     break;
  1297.                 case FIGHTER:
  1298.                 case BOMBER:
  1299.                 case AIRCAV:
  1300.                     // We can only attack these if we can go on it's
  1301.                     //   terrain.
  1302.                     if((movement_cost_table[attacker][def_terrain]>0)||def_road )
  1303.                         return 1;
  1304.                     else
  1305.                         return 0;
  1306.                     break;
  1307.                 default:
  1308.                     return 0;
  1309.                     break;
  1310.             }
  1311.             break;
  1312.         case FIGHTER:
  1313.             switch( defender ) {
  1314.                 case MILITIA:
  1315.                 case ARMOR:
  1316.                 case RIFLE:
  1317.                 case FIGHTER:
  1318.                 case BOMBER:
  1319.                 case AIRCAV:
  1320.                 case TRANSPORT:
  1321.                 case SUB:
  1322.                 case DESTROYER:
  1323.                 case BATTLESHIP:
  1324.                 case CRUISER:
  1325.                 case CARRIER:
  1326.                     // We can attack anyone
  1327.                     return 1;
  1328.                     break;
  1329.                 default:
  1330.                     // Except cities, mines, airfields...
  1331.                     return 0;
  1332.                     break;
  1333.             }
  1334.             break;
  1335.         case BOMBER:
  1336.             switch( defender ) {
  1337.                 case CITY:
  1338.                 case AIRBASE:
  1339.                 case MILITIA:
  1340.                 case ARMOR:
  1341.                 case RIFLE:
  1342.                 case BOMBER:
  1343.                 case AIRCAV:
  1344.                 case TRANSPORT:
  1345.                 case SUB:
  1346.                 case DESTROYER:
  1347.                 case BATTLESHIP:
  1348.                 case CRUISER:
  1349.                 case CARRIER:
  1350.                     // We can attack anyone
  1351.                     return 1;
  1352.                     break;
  1353.                 default:
  1354.                     // Except fighters, mines
  1355.                     return 0;
  1356.                     break;
  1357.             }
  1358.             break;
  1359.         case AIRCAV:
  1360.             switch( defender ) {
  1361.                 case CITY:
  1362.                 case AIRBASE:
  1363.                 case MILITIA:
  1364.                 case ARMOR:
  1365.                 case RIFLE:
  1366.                 case BOMBER:
  1367.                 case AIRCAV:
  1368.                 case TRANSPORT:
  1369.                 case SUB:
  1370.                 case DESTROYER:
  1371.                 case BATTLESHIP:
  1372.                 case CRUISER:
  1373.                 case CARRIER:
  1374.                     // We can attack anyone
  1375.                     return 1;
  1376.                     break;
  1377.                 default:
  1378.                     // Except mines
  1379.                     return 0;
  1380.                     break;
  1381.             }
  1382.             break;
  1383.         case TRANSPORT:
  1384.         case DESTROYER:
  1385.         case SUB:
  1386.         case CARRIER:
  1387.             switch( defender ) {
  1388.                 // We can always attack ships
  1389.                 case TRANSPORT:
  1390.                 case DESTROYER:
  1391.                 case CRUISER:
  1392.                 case BATTLESHIP:
  1393.                 case CARRIER:
  1394.                     return 1;
  1395.                     break;
  1396.                 case RIFLE:
  1397.                 case ARMOR:
  1398.                 case MILITIA:
  1399.                     // We cannot attack ground units
  1400.                     return 0;
  1401.                     break;
  1402.                 case FIGHTER:
  1403.                 case BOMBER:
  1404.                 case AIRCAV:
  1405.                     // We can attack these if we can go there
  1406.                     if( movement_cost_table[ attacker ][ def_terrain ] > 0 )
  1407.                         return 1;
  1408.                     else
  1409.                         return 0;
  1410.                     break;
  1411.                 default:
  1412.                     return 0;
  1413.                     break;
  1414.             }
  1415.             break;
  1416.         case CRUISER:
  1417.         case BATTLESHIP:
  1418.             switch( defender ) {
  1419.                 // We can always attack ships
  1420.                 case TRANSPORT:
  1421.                 case DESTROYER:
  1422.                 case CRUISER:
  1423.                 case BATTLESHIP:
  1424.                 case CARRIER:
  1425.                     return 1;
  1426.                     break;
  1427.                 case RIFLE:
  1428.                 case ARMOR:
  1429.                 case MILITIA:
  1430.                     // We can always bombard ground units
  1431.                     return 1;
  1432.                     break;
  1433.                 case FIGHTER:
  1434.                 case BOMBER:
  1435.                 case AIRCAV:
  1436.                     // for aircraft we need to make sure we
  1437.                     //  can go there.
  1438.                     if( movement_cost_table[ attacker ][ def_terrain ] > 0 )
  1439.                         return 1;
  1440.                     else
  1441.                         return 0;
  1442.                     break;
  1443.                 default:
  1444.                     return 0;
  1445.                     break;
  1446.              }
  1447.             break;
  1448.         default:
  1449.             // Covers city, airbase, mine, etc.
  1450.             // They can never attack.
  1451.             return 0;
  1452.             break;
  1453.   }
  1454.   // Should never get here unless I slip a case
  1455.   return 0;
  1456. }
  1457.  
  1458.  
  1459. int  AI5_unit_value ( short type )
  1460. {
  1461.     int value = 0;
  1462.     /* For now we will keep the value equal to the amount of effort
  1463.        needed to build one of them.  This can be expanded later to
  1464.        take different production efficiencies into account (since
  1465.        if your enemy can make something 'cheaper' than you can, it
  1466.        will not be as valuable to him as it is to you - e.g. if you
  1467.        both have an ARMOR unit, and his only tool him 8 turns to
  1468.        make rather than the 12 it took you, his ARMOR unit means
  1469.        less to him - is more quickly replaced - than yours)as well
  1470.        as other factors I haven't thought of yet.
  1471.        */
  1472.     if (type == CITY) return 10000;
  1473.  
  1474.     value = wishbook[type].build;
  1475.     /* Add extra amounts to Transport and Carrier to reflect 1/2 load
  1476.        of the most expensive units each can hold.
  1477.        */
  1478.     if (type == CARRIER) value += 2 * wishbook[ARMOR].build;
  1479.     if (type == TRANSPORT) value += 4 * wishbook[FIGHTER].build;
  1480.     return (value);
  1481. }
  1482.  
  1483.  
  1484. int  AI5_evaluate_odds ( struct Unit* unit, short type, short x, short y,
  1485.           int enemy_player)
  1486. {
  1487.     /* This returns the chances out of 100 that we will win in an
  1488.        attack (i.e. the odds we will be left and the enemy unit will be
  1489.        destroyed) - a 99 would be almost certain victory, while a 5 would
  1490.        be almost certain destruction.  NOTE: my methods here may not be
  1491.        statistically or mathematically correct, but should give an
  1492.        interesting result anyway.
  1493.        */
  1494.     /* This needs to take into account the attack efficiencies of both
  1495.        players, the damages to this players unit (if any), the terrain
  1496.        factors (if any), and whether the attack can take place at all.
  1497.        */
  1498.  
  1499.     struct Unit Enemy;  // A dummy enemy unit for using the
  1500.                          //NCS_combat_mods routine
  1501.     int     att_value,  def_value;
  1502.     double  att_to_hit, def_to_hit;
  1503.     double  results;
  1504.     //double  att_hit_percentage, def_hit_percentage;
  1505.     static  int strength[] = { 1, 1, 1, 2, 1, 1, 3, 1, 2, 3, 1, 1 };
  1506.  
  1507.     /*
  1508.          strength is the amount of damage a hit from the unit does
  1509.          infantry --- 1
  1510.          armor ------ 1
  1511.          aircav ----- 1
  1512.          bomber ----- 2
  1513.          fighter ---- 1
  1514.          transport -- 1
  1515.          sub -------- 3
  1516.          destroyer -- 1
  1517.          cruiser ---- 2
  1518.          battleship - 3
  1519.          carrier ---- 1
  1520.     */
  1521.  
  1522.     // Can we attack at all?  If not, return -1
  1523.     if( !AI5_can_attack( unit->type, type, x, y ) )  return -1;
  1524.  
  1525.     /* First, let's get the raw numbers on the combat */
  1526.  
  1527.     /* But before we do that we need to create a 'dummy' unit to use
  1528.        in our call to the NCS routine. */
  1529.     Enemy.type = type;
  1530.     Enemy.col = x;
  1531.     Enemy.row = y;
  1532.     Enemy.owner = enemy_player;
  1533.     Enemy.name = NULL;
  1534.     Enemy.orders = NULL;
  1535.  
  1536.     NCS_combat_mods( unit, &Enemy, &att_value, &def_value );
  1537.  
  1538.     // Let's limit these so we don't crash anything
  1539.     if( att_value < -8 ) att_value = -8;
  1540.     if( att_value > 8 ) att_value = 8;
  1541.     if( def_value < -8 ) def_value = -8;
  1542.     if( def_value > 8 ) def_value = 8;
  1543.     /* Now, we can get an idea of the chance for each to hit the other
  1544.        each round. */
  1545.     //att_hit_percentage = 51 + ( att_value * 6.25);
  1546.     //def_hit_percentage = 51 + ( def_value * 6.25);
  1547.     // These will be numbers from 1 to 101 at the extremes.
  1548.  
  1549.     // Now we need to ratio them, as only the relative is used (someone
  1550.     //   always hits someone every 'round' of combat)
  1551.     //att_to_hit = att_hit_percentage/(att_hit_percentage + def_hit_percentage);
  1552.     //def_to_hit = def_hit_percentage/(att_hit_percentage + def_hit_percentage);
  1553.     att_to_hit = AI5_CalcOddsAdjust( att_value - def_value );
  1554.  
  1555.     // Now we call my handy-dandy recursive odds calculating routine
  1556.     results = AI5_CalcWinOdds( 1.0, // was 1.0
  1557.         (wishbook[unit->type].hitpoints - unit->damage),
  1558.         (wishbook[type].hitpoints), strength[unit->type],
  1559.         strength[type], att_to_hit);
  1560.  
  1561.     /* As a check, these two should sum to 1.0 +/- a trifle. WARNING -
  1562.        if the defender to hit chance is zero we are in trouble as we'll
  1563.        divide by zero in a moment. */
  1564.     //if( def_to_hit == 0.0 ) return 100; /* No chance to lose */
  1565.     //if( att_to_hit == 0.0 ) return 0; /* No chance to win */
  1566.  
  1567.     /* Next, using those numbers we find out a number of rounds each needs
  1568.        to kill the other based on their to-hit chance, the amount of damage
  1569.        each does with a hit, and the hits it takes to kill each one */
  1570.     //att_rtk = ((float)wishbook[type].hitpoints) / (att_to_hit *
  1571.    //      ((float)strength[unit->type]) );
  1572.     //def_rtk = ((float)(wishbook[unit->type].hitpoints - unit->damage)) /
  1573.    //       (def_to_hit * ((float)strength[type]) );
  1574.     /* Here we gave the enemy the benefit of the doubt and count him at full
  1575.        strength, and for our unit be realistic and use our current hits.
  1576.        */
  1577.  
  1578.     /* Return the chance we have to LOSE the fight, expressed as a ratio
  1579.        of the attackers rounds to kill over the total - should go from
  1580.        a limit of 99 down to 0. */
  1581.     // for example, if it will take the enemy 16 rounds to kill us (say we
  1582.     //   are a carrier), and it takes 4 rounds for us to kill them (they
  1583.     //   are a fighter) all things being equal, then we return 16/(16+4) *100
  1584.     //   = 80, or an 80% chance to win.
  1585.     return ( (int) (results * 100.0) );
  1586. }
  1587.  
  1588. double   AI5_CalcWinOdds( double Prob, int Hits, int EHits,
  1589.    int Damage, int EDamage, double ToHit)
  1590. {
  1591.    double Win;
  1592.    double Lost;
  1593.    // This is done from the point of view of the attacker
  1594.    // If we are dead, we lost - return 0.  If the enemy is
  1595.    //   dead we win, so return 1.0
  1596.    if( Hits <= 0 ) return 0.0;
  1597.    if( EHits <= 0 ) return (1.0); // tried 1.0 * ToHit
  1598.    // Now we call the routine recursively to get the results of
  1599.    //   our winning and losing this round.
  1600.    Win = Prob * ToHit * AI5_CalcWinOdds  // Tried taking out the *ToHit
  1601.       ( Prob, Hits, (EHits - Damage), Damage, EDamage, ToHit );
  1602.         // Was Prob*ToHit in call
  1603.    Lost = Prob * (1.0 - ToHit) * AI5_CalcWinOdds // Tried taking out *(1.0-ToHit)
  1604.       ( Prob, (Hits - EDamage), EHits, Damage, EDamage, ToHit );
  1605.         // Was Prob*(1.0-ToHit) in call
  1606.    return (Win + Lost);
  1607. }
  1608.  
  1609. double  AI5_CalcOddsAdjust( int att_adj )
  1610. {
  1611.     switch (att_adj * 2) {  // I think the *2 will more accurately show odds
  1612.         case 15:
  1613.             return 0.9975;
  1614.         case 14:
  1615.             return 0.9954;
  1616.         case 13:
  1617.             return 0.9885;
  1618.         case 12:
  1619.             return 0.9815;
  1620.         case 11:
  1621.             return 0.9676;
  1622.         case 10:
  1623.             return 0.9537;
  1624.         case 9:
  1625.             return 0.9306;
  1626.         case 8:
  1627.             return 0.9075;
  1628.         case 7:
  1629.             return 0.8727;
  1630.         case 6:
  1631.             return 0.8380;
  1632.         case 5:
  1633.             return 0.7890;
  1634.         case 4:
  1635.             return 0.7401;
  1636.         case 3:
  1637.             return 0.6825;
  1638.         case 2:
  1639.             return 0.6250;
  1640.         case 1:
  1641.             return 0.5625;
  1642.         case 0:
  1643.             return 0.5000;
  1644.         case -1:
  1645.             return 0.4375;
  1646.         case -2:
  1647.             return 0.3750;
  1648.         case -3:
  1649.             return 0.3170;
  1650.         case -4:
  1651.             return 0.2590;
  1652.         case -5:
  1653.             return 0.2105;
  1654.         case -6:
  1655.             return 0.1620;
  1656.         case -7:
  1657.             return 0.1273;
  1658.         case -8:
  1659.             return 0.0926;
  1660.         case -9:
  1661.             return 0.0695;
  1662.         case -10:
  1663.             return 0.0463;
  1664.         case -11:
  1665.             return 0.0324;
  1666.         case -12:
  1667.             return 0.0185;
  1668.         case -13:
  1669.             return 0.0115;
  1670.         case -14:
  1671.             return 0.0046;
  1672.         case -15:
  1673.             return 0.0023;
  1674.         default:
  1675.             if(att_adj >= 16)  return 1.0;
  1676.             else  return 0.0;
  1677.     }
  1678. }
  1679.  
  1680.  
  1681. /***************************************************************
  1682. *************** Decision Routines  *****************************
  1683. ***************************************************************/
  1684.  
  1685.  
  1686.  
  1687. void  AI5_give_orders( )
  1688. {
  1689.     struct  Unit   *unit = (struct Unit *)unit_list.mlh_Head;
  1690.     struct GovNode *Gov = NULL;
  1691.  
  1692.     for (;unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  1693.         if ((unit->owner == player) && (unit->move > 0) &&
  1694.             (unit->orders == NULL)) {
  1695.             /* No orders yet, let's set some
  1696.           Ok, we own the unit, and it has moves left, and has a Governor
  1697.           owner, and has no standing orders
  1698.           Don't just stand there, do something!
  1699.           */
  1700.            //sprintf(outbuf,
  1701.            //"Giving initial orders to unit named %s at %ld,%ld",
  1702.             //   unit->name, unit->col, unit->row);
  1703.             //   DEBUG_AI(outbuf)
  1704.  
  1705.             Gov = AI1_FindOwner (unit);
  1706.             if (Gov != NULL) {
  1707.            // Issue orders depending on the Governor type
  1708.            switch( Gov->type ) {
  1709.         case GOV_CITY:
  1710.           AI5_city_orders( Gov, unit );
  1711.           break;
  1712.         case GOV_PORT:
  1713.           AI5_port_orders( Gov, unit );
  1714.           break;
  1715.         case GOV_ISLAND:
  1716.           AI5_island_orders( Gov, unit );
  1717.           break;
  1718.         case GOV_TRANSPORT:
  1719.           AI5_transport_orders( Gov, unit );
  1720.           break;
  1721.         case GOV_CARRIER:
  1722.           AI5_carrier_orders( Gov, unit );
  1723.           break;
  1724.         case GOV_BATTLESHIP:
  1725.           AI5_battleship_orders( Gov, unit );
  1726.           break;
  1727.       }
  1728.        }
  1729.        else {
  1730.            AI5_default_orders( unit );
  1731.        }
  1732.    }
  1733.     // End for loop
  1734. }
  1735.  
  1736.  
  1737. void AI5_city_orders( struct GovNode* Gov, struct Unit* unit )
  1738. {
  1739.     struct GovNode* Gov2;
  1740.     struct MapIcon* icon;
  1741.     switch (Gov->mode) {
  1742.        case GOV_SUPPORT:
  1743.      // Here we want to evaluate the situation and load units to
  1744.      //  other Govs if they are in need (and close enough).
  1745.           switch( unit->type ) {
  1746.         case RIFLE:
  1747.           break;
  1748.         case ARMOR:
  1749.           break;
  1750.         case FIGHTER:
  1751.           break;
  1752.         case BOMBER:
  1753.           break;
  1754.         case AIRCAV:
  1755.           break;
  1756.         default:
  1757.             break;
  1758.      }
  1759.           break;
  1760.        case GOV_SEARCH:
  1761.      // We want to scout around us - find new cities to conquer
  1762.           switch( unit->type ) {
  1763.         case RIFLE:
  1764.           //break  Fall through
  1765.         case ARMOR:
  1766.           /* Let's do some self preservation - check if we are hurt */
  1767.           if( unit->damage > 0 ) {
  1768.        /* We're damaged - head for home to get repaired */
  1769.        AI4_computer_give_orders
  1770.          ( unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, 10 );
  1771.           }
  1772.           else {
  1773.        /* let's try to find something! */
  1774.        if( RangeRand(10L) >= 5 )
  1775.          AI4_computer_give_orders
  1776.            (unit, C_ORDER_RANDOM, -1, -1, -1, -1, -1);
  1777.        else {
  1778.          if( RangeRand(10L) >= 5 )
  1779.            AI4_computer_give_orders
  1780.              (unit, C_ORDER_WALK_COASTLINE, 0, 0, -1, -1, 0);
  1781.          else
  1782.            AI4_computer_give_orders
  1783.              (unit, C_ORDER_WALK_COASTLINE, 0, 0, -1, -1, 1);
  1784.        }
  1785.           }
  1786.           break;
  1787.         case FIGHTER:
  1788.           //break;  Fall through
  1789.         case BOMBER:
  1790.           /* Let's go exploring */
  1791.           if( (unit->col == Gov->x) && (unit->row == Gov->y) ) {
  1792.        AI4_computer_give_orders
  1793.          (unit, C_ORDER_RECON, -1, -1, Gov->x, Gov->y, -1 );
  1794.           }
  1795.           else {
  1796.        /* return for fuel */
  1797.        AI4_computer_give_orders
  1798.          (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, unit->fuel);
  1799.           }
  1800.           break;
  1801.         case AIRCAV:
  1802.           /* Except for AirCav - put them in storage until needed */
  1803.           if( city_hereP( unit->col, unit->row ) != NULL ) {
  1804.        /* We are in a city, so go to sentry */
  1805.        AI4_computer_give_orders
  1806.          (unit, C_ORDER_SENTRY, -1, -1, -1, -1, -1);
  1807.           }
  1808.           else {
  1809.        /* Try to get to a city */
  1810.        if( AI5_GetDist (unit->col, unit->row, Gov->x, Gov->y)
  1811.            > unit->fuel ) {
  1812.          /* Need to get to the nearest city-gov and get put in
  1813.             storage there */
  1814.          Gov2 = AI3_FindClosestCityGov( unit, unit->fuel );
  1815.          if( Gov2 ) {
  1816.            AI4_computer_give_orders
  1817.              (unit, C_ORDER_GOTO, Gov2->x, Gov2->y,
  1818.          -1, -1, unit->fuel);
  1819.          }
  1820.          else {
  1821.            /* Now what ?  Try to take an enemy city of course ..*/
  1822.            icon = AI3_FindClosestEnemyCity( unit, unit->fuel );
  1823.            if( icon ) {
  1824.              /* Go get it! */
  1825.              AI4_computer_give_orders
  1826.           (unit, C_ORDER_GOTO, icon->col, icon->row,
  1827.            -1, -1, unit->fuel);
  1828.            }
  1829.            else {
  1830.              /* How can this be?  Why are we out here? We never
  1831.            leave the nest unless attacking a city. */
  1832.              DEBUG_AI5
  1833.           ("AirCav movement problem - can't reach any city")
  1834.            }
  1835.          }
  1836.        }
  1837.        else {
  1838.          /* go home */
  1839.          AI4_computer_give_orders
  1840.            (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, unit->fuel);
  1841.        }
  1842.           }
  1843.           break;
  1844.         default:
  1845.             break;
  1846.      }
  1847.      break;
  1848.        case GOV_DEFEND:
  1849.      // We have enemy units or cities nearby - go after them.
  1850.           switch( unit->type ) {
  1851.         case RIFLE:
  1852.           break;
  1853.         case ARMOR:
  1854.           break;
  1855.         case FIGHTER:
  1856.           break;
  1857.         case BOMBER:
  1858.           break;
  1859.         case AIRCAV:
  1860.           break;
  1861.         default:
  1862.             break;
  1863.      }
  1864.      break;
  1865.        case GOV_TAKEN:
  1866.      // We lost our city.  Try to retake it.
  1867.           switch( unit->type ) {
  1868.         case RIFLE:
  1869.           break;
  1870.         case ARMOR:
  1871.           break;
  1872.         case FIGHTER:
  1873.           break;
  1874.         case BOMBER:
  1875.           break;
  1876.         case AIRCAV:
  1877.           break;
  1878.         default:
  1879.             break;
  1880.      }
  1881.      break;
  1882.        default:
  1883.      break;
  1884.     }
  1885. }
  1886.  
  1887. void AI5_port_orders( struct GovNode* Gov, struct Unit* unit )
  1888. {
  1889.     switch (Gov->mode) {
  1890.        case GOV_SUPPORT:
  1891.           break;
  1892.        case GOV_SEARCH:
  1893.      break;
  1894.        case GOV_DEFEND:
  1895.      break;
  1896.        case GOV_TAKEN:
  1897.      break;
  1898.        default:
  1899.      break;
  1900.     }
  1901. }
  1902.  
  1903. void AI5_island_orders( struct GovNode* Gov, struct Unit* unit )
  1904. {
  1905.     switch (Gov->mode) {
  1906.        case GOV_SUPPORT:
  1907.           break;
  1908.        case GOV_SEARCH:
  1909.      break;
  1910.        case GOV_DEFEND:
  1911.      break;
  1912.        case GOV_TAKEN:
  1913.      break;
  1914.        default:
  1915.      break;
  1916.     }
  1917. }
  1918.  
  1919. void AI5_transport_orders( struct GovNode* Gov, struct Unit* unit )
  1920. {
  1921.     switch (Gov->mode) {
  1922.        case GOV_ATTACK:
  1923.      break;
  1924.        case GOV_DEFEND:
  1925.      break;
  1926.        case GOV_OUTNUMBERED:
  1927.      break;
  1928.        default:
  1929.      break;
  1930.     }
  1931. }
  1932.  
  1933. void AI5_carrier_orders( struct GovNode* Gov, struct Unit* unit )
  1934. {
  1935.     switch (Gov->mode) {
  1936.        case GOV_ATTACK:
  1937.      break;
  1938.        case GOV_DEFEND:
  1939.      break;
  1940.        case GOV_OUTNUMBERED:
  1941.      break;
  1942.        default:
  1943.      break;
  1944.     }
  1945. }
  1946.  
  1947. void AI5_battleship_orders( struct GovNode* Gov, struct Unit* unit )
  1948. {
  1949.     switch (Gov->mode) {
  1950.        case GOV_ATTACK:
  1951.      break;
  1952.        case GOV_DEFEND:
  1953.      break;
  1954.        case GOV_OUTNUMBERED:
  1955.      break;
  1956.        default:
  1957.      break;
  1958.     }
  1959. }
  1960.  
  1961. void AI5_default_orders( struct Unit* unit )
  1962. {
  1963. }
  1964.  
  1965. /***************************************************************
  1966. *************** Orders Routines  *******************************
  1967. ***************************************************************/
  1968.  
  1969.  
  1970. void AI5_command_goto( struct Unit* unit )
  1971. {
  1972. }
  1973.